package com.cuiweiyou.arduinogirl.bluetooth;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.content.Context;
import android.util.Log;

import com.cuiweiyou.arduinogirl.base.BluetoothApp;
import com.cuiweiyou.arduinogirl.model.EventArduinoModel;
import com.cuiweiyou.arduinogirl.model.EventDeviceModel;
import com.cuiweiyou.arduinogirl.model.EventGattedModel;
import com.cuiweiyou.arduinogirl.util.StringUtil;

import org.greenrobot.eventbus.EventBus;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * Created by macpro on 2018/10/24.
 */

public class BluetoothMaster {
    private static BluetoothMaster instance;
    private BluetoothAdapter bluetoothAdapter;
    private BluetoothLeScanner bluetoothLeScanner;
    private Map<String, ScanResult> bluetoothDeviceMap;
    private List<ScanResult> bluetoothDeviceList;
    private BluetoothGatt currentBluetoothGatt;
    private BluetoothManager bluetoothManager;
    private ScanResult targetDevice;
    private OnCharacteristicWritedListener onCharacteristicWritedListener;
    private String lastToBluetoothMsg = "";
    private List<BluetoothGattService> gattServiceList;
    private BluetoothGattCharacteristic bluetoothHandlerCharacteristic;

    private BluetoothMaster() {
        bluetoothDeviceMap = new HashMap<>();
        bluetoothDeviceList = new ArrayList<>();
    }

    public static BluetoothMaster getInstance() {
        if (null == instance) {
            synchronized (BluetoothMaster.class) {
                if (null == instance) {
                    instance = new BluetoothMaster();
                }
            }
        }
        return instance;
    }

    public boolean isDeviceEnable() {
        bluetoothManager = (BluetoothManager) BluetoothApp.getContext().getSystemService(Context.BLUETOOTH_SERVICE);
        bluetoothAdapter = bluetoothManager.getAdapter();

        if (bluetoothAdapter == null) {
            return false;
        }

        return true;
    }

    public boolean isBluetoothEnable() {
        if (!bluetoothAdapter.isEnabled() || BluetoothAdapter.STATE_OFF == bluetoothAdapter.getState()) {
            return false;
        }
        return true;
    }

    // 1.扫描设备
    public void scan() {
        Log.e("ard", "开始扫描蓝牙设备");
        bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
        bluetoothLeScanner.startScan(scanCallback);
    }

    /**
     * 2.扫描结果返回
     */
    private ScanCallback scanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            if (null != result) {
                BluetoothDevice device = result.getDevice();
                String name = device.getName();
                String address = device.getAddress();
                if (null == bluetoothDeviceMap.get(address)) {
                    Log.e("ard", "设备name：" + name + "，address：" + address);
                }

                bluetoothDeviceMap.put(address, result); // 已有的设备被覆盖，刷新信号强度
                if (null != bluetoothDeviceList) {
                    bluetoothDeviceList.clear();
                }
                bluetoothDeviceList = BluetoothRssiSortor.sortDevicesByRssi(bluetoothDeviceMap);
                EventBus.getDefault().postSticky(new EventDeviceModel());
            }
        }

        public void onBatchScanResults(List<ScanResult> results) {
            Log.e("ard", "onBatchScanResults：" + results.size());
        }

        public void onScanFailed(int errorCode) {
            Log.e("ard", "onScanFailed errorCode：" + errorCode);
        }
    };

    // 3.连接某个蓝牙
    public void connectDevice(ScanResult model) {
        this.targetDevice = model;

        final BluetoothDevice device = model.getDevice();
        String name = device.getName();
        String address = device.getAddress();

        Log.e("ard", "选择设备：" + name + "  " + address);

        device.connectGatt(BluetoothApp.getContext(), true, gattCallback); // 连接这台蓝牙设备
    }

    // 4.断开已经连接的蓝牙
    public void dissConnectDevice(int flag) {
        Log.e("ard", "断开设备：" + targetDevice.getDevice().getName());

        bluetoothDeviceMap.remove(targetDevice.getDevice().getAddress()); // 已有的设备被覆盖，刷新信号强度
        if (0 == flag) {
            bluetoothDeviceList.clear();
            bluetoothDeviceList = null;
        } else {
            bluetoothDeviceList = BluetoothRssiSortor.sortDevicesByRssi(bluetoothDeviceMap);
            EventBus.getDefault().postSticky(new EventDeviceModel());
        }
        if (null != currentBluetoothGatt) {
            currentBluetoothGatt.disconnect();
            currentBluetoothGatt.close();
        }

        if (null != gattServiceList) {
            gattServiceList.clear();
        }

        currentBluetoothGatt = null;
        gattServiceList = null;
        targetDevice = null;
    }

    /**
     * 退出app时释放
     */
    public void release() {
        dissConnectDevice(0);
    }

    // 5.获取已连接设备的Service
    public void getGattDeviceServices() {
        if (null != currentBluetoothGatt) {
            currentBluetoothGatt.discoverServices(); // 扫描开放的服务。回调BluetoothGattCallback对象的onServicesDiscovered方法
        }
    }

    //连接成功后回调此GATT对象
    private BluetoothGattCallback gattCallback = new BluetoothGattCallback() {

        /**
         * BluetoothGattCallback中的方法。获取和蓝牙设备的连接状态
         * @param gatt 返回连接建立的gatt对象
         * @param status 返回的是此次gatt操作的结果，成功了返回0
         * @param newState 每次client连接或断开连接状态变化
         */
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            Log.e("ard", "连接状态改变：" + status + "，新状态：" + newState + "，GATT：" + gatt.hashCode());
            if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothGatt.STATE_CONNECTED) { // 0-gatt连接成功
                currentBluetoothGatt = gatt;
                bluetoothLeScanner.stopScan(scanCallback); // 停止扫描
                EventBus.getDefault().post(new EventGattedModel(1)); // 通知UI连接了设备
            } else { // 失败或断开。这个状态的回调比较慢
                Log.e("ard", "连接状态改变：失败或断开");

                dissConnectDevice(1);
                EventBus.getDefault().post(new EventGattedModel(0)); // 通知UI连接异常
                scan();
            }
        }

        /**
         * BluetoothGattCallback中的方法。获取蓝牙设备开放的服务
         * 当执行 currentBluetoothGatt.discoverServices(); 后才可以获取到蓝牙设备的服务
         * @param gatt 返回的是本次连接的gatt对象，即currentBluetoothGatt
         * @param status
         */
        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            gattServiceList = gatt.getServices();
            Log.e("ard", "蓝牙模块服务开放状态：" + status + "，GATT：" + gatt.hashCode() + "，服务数量：" + gattServiceList.size());

            // 遍历Service
            for (int i = 0; i < gattServiceList.size(); i++) {
                BluetoothGattService gattService = gattServiceList.get(i);
                String serviceUUID = gattService.getUuid().toString();
                List<BluetoothGattCharacteristic> characteristicList = gattService.getCharacteristics();
                Log.e("ard", "服务UUID：" + serviceUUID + "，其下特征码数量： " + (null == characteristicList ? 0 : characteristicList.size()));

                // 遍历Characteristic
                for (int j = 0; j < characteristicList.size(); j++) {
                    BluetoothGattCharacteristic characteristic = characteristicList.get(j);
                    currentBluetoothGatt.setCharacteristicNotification(characteristic, true); // 设置可接收回调消息
                    String characteristicUUID = characteristic.getUuid().toString();
                    int properties = characteristic.getProperties();
                    Log.e("ard", "\t特征码UUID：" + characteristicUUID + "，属性：" + properties);

                    // 这个特征码是手机向蓝牙发数据的。根据蓝牙模块型号不同特征码uuid也不同
                    if (characteristicUUID.startsWith("0000ffe2")) {
                        bluetoothHandlerCharacteristic = characteristic;
                    }

                    // --- 以下if块没有实质操作 ---
                    if ((properties | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) { // Characteristic可以接收回调消息
                        List<BluetoothGattDescriptor> descriptorList = characteristic.getDescriptors();
                        if (null != descriptorList && descriptorList.size() > 0) {

                            // 遍历Descriptor
                            for (int k = 0; k < descriptorList.size(); k++) {
                                BluetoothGattDescriptor descriptor = descriptorList.get(k);
                                UUID descriptorUUID = descriptor.getUuid();
                                byte[] descriptorValue = descriptor.getValue();
                                Log.e("ard", "\t\t描述符UUID：" + descriptorUUID + "，" + String.valueOf(descriptorValue));
                            }
                        }
                    }
                }
            }
        }

        /**
         * 从蓝牙模块读取
         * Arduino向蓝牙串口写输入后响应
         *
         * @param gatt
         * @param characteristic
         */
        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            byte[] data = characteristic.getValue();
            String msg = StringUtil.getChars(data);
            EventBus.getDefault().post(new EventArduinoModel(msg)); // 将Arduino发来的消息发到UI
            Log.e("ard", "通过蓝牙收到Arduino的数据：" + msg);
        }

        /**
         * 向蓝牙模块写入
         * 手机向蓝牙write操作后回调
         *
         * @param gatt
         * @param characteristic
         * @param status
         */
        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            Log.e("ard", "通过蓝牙向Arduino发送了数据：" + status);

            if (null != onCharacteristicWritedListener) {
                onCharacteristicWritedListener.onCharacteristicWrited(characteristic, status, lastToBluetoothMsg);
            }
        }
    };

    /**
     * 扫描到的全部蓝牙设备
     *
     * @return
     */
    public List<ScanResult> getDevices() {
        return bluetoothDeviceList;
    }

    /**
     * 当前连接的蓝牙设备
     *
     * @return
     */
    public ScanResult getGattDevice() {
        return targetDevice;
    }

    /**
     * 向蓝牙模块发消息
     *
     * @param listener
     * @param msg
     */
    public void sendMsgToBlueTooth(OnCharacteristicWritedListener listener, String msg) {
        lastToBluetoothMsg = msg;
        onCharacteristicWritedListener = listener;
        bluetoothHandlerCharacteristic.setValue(msg);
        currentBluetoothGatt.writeCharacteristic(bluetoothHandlerCharacteristic); // 回调BluetoothGattCallback的onCharacteristicWrite方法
    }

    /**
     * 向蓝牙模块发消息回调
     */
    public interface OnCharacteristicWritedListener {
        /**
         * 向蓝牙模块发消息回调
         */
        void onCharacteristicWrited(BluetoothGattCharacteristic characteristic, int status, String msg);
    }

}
